// scaup - Scala up!
// (c) 2008-2010, Normen Müller, normen.mueller@gmail.com  
// $Id: Parser.scala 78 2010-05-13 12:25:41Z normen.mueller $
package scaup.parsing.eq

import java.io.File

import scala.util.parsing.combinator._
import scala.util.parsing.combinator.syntactical._

import scaup.Views._

import scaup.eq.{EquivalenceSpecification, Alternatives, Constraints}

/** An equivalence system parser.
 *
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.4
 */
private [eq] class Parser extends StdTokenParsers with ImplicitConversions {  
  /* Fill in abstract defs
   */
  type Tokens = Lexer
  val lexical: Tokens = new Lexer
  
  /* Configure lexical parsing
   */
  lexical.reserved ++= List("equivspec", "extends", "element", "alternative", "annotations", "constituents", "ordered", "unordered", "_", "\\", "<TEXT>")
  lexical.delimiters ++= List("{", "}", ",", "?")
  
  /* Define the grammar
   */
  //@todo lazy val eqsys = ("equivspec" ~> ident) ~ opt(eqext) ~ "{" ~ (eqelems*) ~ "}" ^^ {
  lazy val eqsys = ("equivspec" ~> ident) ~ ("{" ~> (eqelem*) <~ "}") ^^ {
    case name ~ elems =>
      val spec = (Map[String, Alternatives]() /: elems)((acc, e) => (acc ++ e._2))     
      val uord = (Set[String]() /: elems)((o, e) => e._1 match {
        case Some(e) => o + e
        case None    => o
      })
      EquivalenceSpecification(name, spec, uord)
  }
  
  //@todo lazy val eqdtd = "for" ~ url
  
  //@todo lazy val eqext = "extends" ~ ident
  
  lazy val eqelem = 
    (  (opt("unordered") ~ ("element" ~> ident) ~ ("{" ~> eqalt <~ "}")) ^^ { case ord ~ name ~ alt => ord match { 
         case Some(_) => (Some(name), Map[String, Alternatives](name -> alt))
         case None    => (None, Map[String, Alternatives](name -> alt))
    } } 
    |  (opt("unordered") ~ ("element" ~> ident) ~ eqbody) ^^ { case ord ~ name ~ body => ord match { 
         case Some(_) => (Some(name), Map[String, Alternatives](name -> body))
         case None    => (None, Map[String, Alternatives](name -> body))
    } } 
    ) 
  
  lazy val eqalt = ("alternative" ~ "{") ~> (eqbody+) <~ "}" ^^ {
    case body => Alternatives((List[Constraints]() /: body)((l, b) => l ::: b.constraints))
  }
  
  lazy val eqbody = ("{" ~> (annos?) ~ (consts?) <~ "}") ^^ {
    case annos ~ consts => (annos, consts) match {
      case (None, Some((reqoc, optoc, requc, optuc)))                 => Alternatives(List(Constraints(Set[String](), Set[String](), reqoc, optoc, requc, optuc)))
      case (Some((reqas, optas)), None)                               => Alternatives(List(Constraints(reqas, optas, Set[String](), Set[String](), Set[String](), Set[String]())))
      case (Some((reqas, optas)), Some((reqoc, optoc, requc, optuc))) => Alternatives(List(Constraints(reqas, optas, reqoc, optoc, requc, optuc)))
      case (None, None)                                               => Alternatives(Nil)
    }
  }
  
  //@todo lazy val annos = "annotations" ~ "{" ~> (anameopts | allbutanames) ~ "}"
  lazy val annos = ("annotations" ~ "{") ~> (aopts <~ "}") ^^ {case (req, opt) => (req, opt)}
  
  lazy val aopts =  (ident ~ opt("?") ~ (("," ~> (ident ~ opt("?")))*)) ^^ {
    case name ~ names =>       
      ((Set[String](), Set[String]()) /: (name :: names))((lst, n) => n match {
        case i ~ Some(o) => (lst._1, lst._2 + i)
        case i ~ None    => (lst._1 + i, lst._2)
      })
  }  
  
  //@todo lazy val allbutanames = "_" ~ "\\" ~ "{" ~ anames ~ "}"  
  //@todo lazy val anames =  (ident ~ (("," ~> ident)*))
  
  lazy val consts = ("constituents" ~ "{") ~> ((oconst?) ~ (uconst?)) <~ "}" ^^ { 
    case oc ~ uc => (oc, uc) match {
      case (Some(oc), Some(uc)) => (oc._1, oc._2, uc._1, uc._2)
      case (None, Some(uc))     => (Set[String](), Set[String](), uc._1, uc._2)
      case (Some(oc), None)     => (oc._1, oc._2, Set[String](), Set[String]())
      case (None, None)         => (Set[String](), Set[String](), Set[String](), Set[String]())
    }
  }
  
  //@todo lazy val oconst = "ordered" ~ "{" ~ (labelopts | allbutlabels) ~ "}"
  lazy val oconst = ("ordered" ~ "{") ~> labelopts <~ "}"

  //@todo lazy val uconst = "unordered" ~ "{" ~ (labelopts | allbutlabels) ~ "}"
  lazy val uconst = ("unordered" ~ "{") ~> labelopts <~ "}"
  
  lazy val labelopts = ((ident | "<TEXT>") ~ opt("?") ~ (("," ~> ((ident | "<TEXT>") ~ opt("?")))*))  ^^ {
    case name ~ names =>       
      ((Set[String](), Set[String]()) /: (name :: names))((lst, n) => n match {
        case i ~ Some(o) => (lst._1, lst._2 + i)
        case i ~ None    => (lst._1 + i, lst._2)
      })
  } 
  
  //@todo lazy val allbutlabels = "_" ~ "\\" ~ "{" ~ labels ~ "}"
  //@todo lazy val labels =  (ident ~ (("," ~> ident)*))
  
  lazy val string = accept("string",  {case lexical.StringLit(n) => n mkString ("\"", "", "\"") })
}